#ifndef DATAMODEL_H_
#define DATAMODEL_H_

#include "scHeader.h"
#include <deque>
#include <QObject>
#include <QTimer>
#include <QString>
#include <QDateTime>
#include "DataTable.h"

namespace sybilpm
{

/**
 * The top-level entry point of the (data) model. Uses singleton
 * pattern.
 */
class DataModel : public QObject
{
	Q_OBJECT
signals:
	/**
	 * General info text about background activities.
	 */
	void statusTextSig(QString text);
	/**
	 * This signal is emitted whenever any entity is added, removed
	 * or modified asynchronously. If data is added or modified
	 * synchronously (during requestData call), this signal is not
	 * emitted.
	 * If transId = 0 all consumer shall react on it otherwise only
	 * the requester of the given transId is expected to react.
	 * @see requestData
	 */
	void dataChangedSig(unsigned long transId);
	/**
	 * This signal is emitted whenever a server response was parsed.
	 * Usually data was added, removed or modified. Use only for monitoring
	 * purpose! Updating views and similar actions should be triggered
	 * by dataChangedSig.
	 * @see dataChangedSig
	 */
	void serverResponseSig();
	/**
	 * Emitted whenever the global deadline was changed. E.g. time-sliced
	 * models shall reload and ts-views shall get redraw.
	 */
	void deadlineChangedSig();
	/**
	 * Emitted when the data model begins a garbage collection cycle. All
	 * consumers of data have to listen to this signal and touch all requests
	 * and DataObjects which they still require.
	 */
	void dataMarkSig();

public:
	/**
	 * singleton access
	 *
	 * @return pointer to the one and only instance
	 */
	static DataModel * get();
	/**
	 * Convenience function for retrieving a parent window if nothing better
	 * than the main window is at hand.
	 */

	DataDomain entityId2Domain(PkType entityId);
	QString entityId2DomainText(PkType entityId);
	const std::string & entityId2Text(PkType entityId) const;
	QString entityId2TextQ(PkType entityId) const;
	PkType text2EntityId(QString entity) const;

	const QDateTime & getDeadlineQ() const {return deadline;}
	TimeType getDeadlineUTC() const {return deadline.toTime_t();}
	void setDeadline(const QDateTime & newDeadline);

	/**
	 * Use this connection for talking to the server.
	 */
	SrvConnection * getSrvConnection() {return srvCon;}
	const SrvConnection * getSrvConnection() const {return srvCon;}

	/**
	 * Ask the data model for "something". Note that such a request may or
	 * may not result in an asynchronous server request - see return value.
	 * dataChangedSig will only be emitted if there is new or modified data
	 * in the model as a result of an asynchronous request.
	 *
	 * @param requDesc defines what we need; DO NOT DELETE, DataModel adopts this object
	 * @return a transaction ID if you have to listen for dataChangedSig or 0 if data is already here.
	 */
	unsigned long requestData(RequestDescriptor * requDesc);

	/**
	 * Call these methods if you think the given object should be in the cache but
	 * you weren't able to locate it. Usually this indicates a too strict (faulty)
	 * garbage collection (like forgetting to touch something).
	 * They are declared as constant so that you can call them from get-methods which
	 * are usually const themself. Anyway the data model may react on this call.
	 */
	void dataMissed(PkType entity, PkType pk) const;
	void dataMissed(PkType entity, PkType ik, TimeType deadline) const;

	QString getRawRequest() const {return rq;}
	QString getRawData() const {return data;}
	const DataTable & getTable(PkType entity) const;
	ModTimeSlice * getModTimeSlice(PkType entity);

public slots:
	void parseDataSl(unsigned long transId);
	void forceGarbageCollectSl();
private:
	typedef std::deque<RequestDescriptor *> RequQueue;
	DataModel(); // intentionally no implementation
	/**
	 * DataModel takes ownership of srvConnection. If the connection is
	 * failed DataModel tries to test.
	 */
	DataModel(SrvConnection & srvConnection);
	void postConstruct();
	~DataModel();
	void keepAlive();
	void reduceRequest(RequestDescriptor * requDesc);
	void dumpRequest(RequestDescriptor *requDesc);
	static DataModel * inst;
	static std::string strNoEntity;
	static std::string strEntities;
	static std::string strEntityAttrs;
	static QString strNoEntityQ;
	SrvConnection * srvCon;
	QTimer timer;
	unsigned long ticks, ticksAlive, ticksGarbage, tickGarbageFin, ticksUpdate;
	unsigned long syncTrans;
	bool bootTrans, queueing;
	RequQueue rqQueue;
	QDateTime deadline;
	DataTables dts;
	ModTimeSlices mts;
	QString rq, data;
private slots:
	void timerTic();
	void checkQueueingSl(bool testSuccess);
};

} // namespace sybilpm

#endif /* DATAMODEL_H_ */
